/// 加密模块
module jinji.util.crypto;

public import blake3;
public import tame.endian;

/// server密钥
extern (C) __gshared ubyte[32] serverKey;

/// 初始化密钥
pragma(inline, true) void initKey(in void[] key) {
	serverKey = blake3Of(key);
}

/// eXtendable Output Function
alias XOF = void function(in void[] input, in ubyte[] key, ubyte[] output) pure;

/// 签名
ubyte[L] sign(size_t L = 32)(in void[] data, in void[] key) {
	return blake3Of!L(data, blake3Of(key, serverKey));
}

pure:

union Block {
	ubyte[64] data;
	struct {
		ulong t;
		ulong u;
	}

	alias data this;
}

/++
XOF-Based Key Derivation Function
Params:
	xor = XOF function
	input = input data
	key = key data
	output = output data
	n = number of iterations
	m = number of blocks
+/
void xbkdf(XOF xof, in void[] input, in ubyte[] key, ubyte[] output, uint n, ulong m)
in (m > 0) {
	import core.exception : onOutOfMemoryError;
	import core.memory;

	Block[] a = (cast(Block*)pureCalloc(Block.sizeof, m))[0 .. m];
	if (!a) {
		onOutOfMemoryError();
	}
	Block* b = &a[0];
	ulong[4] c;
	foreach (t; 0 .. n) {
		const i = b.t % m;
		if (t & 1)
			a[i].t += t;
		else
			xof(*b, key, a[i]);
		c[b.u & 3] = b.t;
		b = &a[i];
	}
	pureFree(a.ptr);
	xof(input, cast(ubyte[])c, output);
}

alias PwdHash = ubyte[33];

void b3(in void[] input, in ubyte[] key, ubyte[] output) {
	import blake3.binding;

	auto hasher = BLAKE3(key[0 .. 32]);
	hasher.put(input);
	blake3_hasher_finalize(&hasher, output.ptr, output.length);
}

/// 密码加密函数
/// 第一个字节为0，剩余的为xBKDF(2390, 32_749)值
auto pwdHash(in void[] pwd, ulong salt) @trusted {
	enum M = 32_749;
	enum N = 2390;

	ubyte[33] hash = void;
	hash[0] = 0;
	const key = blake3Of(toLE(salt));
	xbkdf(&b3, pwd, key, hash[1 .. $], N, M);
	return hash;
}
